
namespace Behemoth.SharePoint.Configuration {

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    using Behemoth;
    using Behemoth.SharePoint.Security;

    public enum ConfigScope
    {
        Web,
        SiteCollection,
        Farm
    }

    public class ConfigurationReader {

      public bool _throwExceptions = true;
      public bool ThrowExceptions {
        get {
          return _throwExceptions;
        }
        set {
          _throwExceptions = value;
        }
      }

      public bool ReadOptionalConfigSetting(SPWeb web, ConfigScope scope, string setting, Type type, object defaultValue, out object value) {
        string valueText = string.Empty;
        // WARN: beware thread crossing calls here, but it's web process so who cares ^_^
        bool oldThrowExceptions = _throwExceptions;
        _throwExceptions = false;
        bool success = ReadConfigSetting(web, scope, setting, type, out value);
        if (!success) {
          bool oldUnsafe = web.AllowUnsafeUpdates;
          web.AllowUnsafeUpdates = true;

          WriteConfigSetting(web, scope, setting, defaultValue.ToString());
          value = defaultValue;

          web.AllowUnsafeUpdates = oldUnsafe;
        }
        _throwExceptions = oldThrowExceptions;
        return success;
      }

      public bool ReadConfigSetting(SPWeb web, ConfigScope scope, string setting, Type type, out object value) {
        value = null;
        string valueText = string.Empty;
        bool success = ReadConfigSetting(web, scope, setting, out valueText);
        if (success) {
          success = Parser.TryParse(valueText, type, ParseFlags.Simple, out value);
        }
        // TODO: how shall we indicate failure to read configuration values? Throw an exception? Log?
        return success;
      }

        /// <summary>
        /// Read a configuration setting from the specified source.
        /// </summary>
        /// <param name="web">SPWeb object for curretn web</param>
        /// <param name="level">Scope at which value is stored.</param>
        /// <param name="setting">Setting to change (key)</param>
        /// <param name="value">Returned value</param>
        /// <returns>True if setting exists and can be read, false otherwise</returns>
        public bool ReadConfigSetting(SPWeb web,ConfigScope scope, string setting, out string value)
        {
            value = string.Empty;
            string retVal = string.Empty;
            bool returnBool = true;
            Delegation.RunWithElevatedPriviliges(web, delegate
            {
                using (SPSite site = new SPSite(web.Site.ID))
                {
                    using (SPWeb elevatedWeb = site.AllWebs[web.ID])
                    {
                        retVal = string.Empty;
                        switch (scope)
                        {
                            case ConfigScope.Web:
                                try
                                {
                                    retVal = elevatedWeb.Properties[setting];
                                }
                                catch (ArgumentException ex)
                                {
                                    returnBool = false;
                                    if (this._throwExceptions)
                                        throw new Exception(string.Format("Failure to read configuration setting '{0}' from property bag for web '{1}'. ", setting, elevatedWeb.Url), ex);
                                }
                                break;
                            case ConfigScope.SiteCollection:
                                try
                                {
                                    retVal = elevatedWeb.Site.RootWeb.Properties[setting];
                                }
                                catch (ArgumentException ex)
                                {
                                    returnBool = false;
                                    if (this._throwExceptions)
                                        throw new Exception(string.Format("Failure to read configuration setting '{0}' from property bag for web '{1}'. ", setting, elevatedWeb.Site.RootWeb), ex);
                                }
                                break;
                            case ConfigScope.Farm:
                                try
                                {
                                    retVal = (string)SPFarm.Local.Properties[setting];
                                }
                                catch (ArgumentException ex)
                                {
                                    returnBool = false;
                                    if (this._throwExceptions)
                                        throw new Exception(string.Format("Failure to read configuration setting '{0}' from property bag for local farm. ", setting), ex);
                                }
                                break;
                        }
                    }
                }
            });
            value = retVal;
            return returnBool;
        }

        /// <summary>
        /// Writes a config value to the web, site or farm
        /// </summary>
        /// <param name="web">SPWeb object for current web</param>
        /// <param name="scope">Scope to write</param>
        /// <param name="setting">Setting (key)</param>
        /// <param name="value">value to write</param>
        public void WriteConfigSetting(SPWeb web,ConfigScope scope, string setting, string value)
        {
             Delegation.RunWithElevatedPriviliges(web, delegate
            {
                using (SPSite site = new SPSite(web.Site.ID))
                {
                    using (SPWeb elevatedWeb = site.AllWebs[web.ID])
                    {
                        switch (scope)
                        {
                            case ConfigScope.Web:
                                elevatedWeb.AllowUnsafeUpdates = true;
                                if (elevatedWeb.Properties.ContainsKey(setting))
                                {
                                    elevatedWeb.Properties[setting] = value;
                                }
                                else
                                {
                                    elevatedWeb.Properties.Add(setting, value);
                                }
                                elevatedWeb.Properties.Update();
                                elevatedWeb.AllowUnsafeUpdates = false;
                                break;
                            case ConfigScope.SiteCollection:
                                elevatedWeb.Site.RootWeb.AllowUnsafeUpdates = true;
                                elevatedWeb.Site.RootWeb.Properties[setting] = value;
                                elevatedWeb.Site.RootWeb.Properties.Update();
                                break;
                            case ConfigScope.Farm:
                                SPFarm.Local.Properties[setting] = value;
                                SPFarm.Local.Update();
                                break;
                        }
                    }
                }
            });
        }
       
    } // class
} // namespace
